package jvs.peer.proxy;

import java.net.ServerSocket;
import java.net.Socket;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import jvs.peer.util.Log;

public abstract class SocketRedirectThread extends Thread {

	private static final String TAG = "SocketRedirectThread";

	protected ExecutorService pool = null;
	
	protected ServerSocket socket = null;

	protected boolean running = false;
	
	protected SocketRedirectThread() {	
	}
	
	public SocketRedirectThread(ServerSocket socket) {
		this.socket = socket;
	}

	public void run() {
		if (socket == null) {
			throw new RuntimeException("socket not initialized");
		}
		pool = Executors.newCachedThreadPool();
		
		running = true;
		
		setName("SocketRedirect");
		try {
			while (running) {
				
				if (Log.DEBUG) {
					Log.d(TAG, socket + " listening: " + socket.getLocalSocketAddress());
				}
				
				Socket in = socket.accept();
				
				if (Log.DEBUG) {
					Log.d(TAG, socket + " accepted: "+ in.getRemoteSocketAddress() + ": " + in.getPort());
				}
				
				handle(in);
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
		
		running = false;
		
		try {
			socket.close();
		} catch (Exception e) {
			e.printStackTrace();
		}
		
		pool.shutdownNow();
		pool = null;
		
		if (Log.DEBUG) {
			Log.d(TAG, socket + " running: " + running);
		}	
	}

	protected abstract void handle(Socket in) throws Exception;

	protected void tunnel(Socket in, Socket out) throws Exception {
		try {
			Runnable t1 = new CopyThread(in.getInputStream(),
					out.getOutputStream());
			Runnable t2 = new CopyThread(out.getInputStream(),
					in.getOutputStream());

			pool.execute(t1);
			pool.execute(t2);

		} catch (Exception e) {

			throw e;
		}
	}

	protected void close(Socket s) {
		if (s == null) {
			return;
		}
		try {
			s.close();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	
	public void cancel() {
		running = false;
		try {
			super.interrupt();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	public boolean isRunning() {
		return running;
	}
	
}
